package com.bnz.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.bnz.search.entity.ItemEntity;
import com.bnz.search.entity.ItemVo;
import com.bnz.search.service.ItemSearch;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class ItemSearchImpl implements ItemSearch {
    @Autowired
    private ElasticsearchClient client;
    @Autowired
    private StringRedisTemplate redisTemplate;

    //1. 根据条件查询商品
    @Override
    public Map search(ItemVo vo) throws IOException {
        //1.1 定义返回的查询结果
        Map resultMap = new HashMap();
        //1.2 定义查询到的商品列表集合
        List<ItemEntity> itemList = new ArrayList<>();
        //1.3 定义存放分类的集合
        List<String> categoryList = new ArrayList<>();
        if (vo != null) {
            //1.4 定义查询响应对象
            SearchResponse<ItemEntity> searchResponse = null;
            //1.5 定义查询请求对象
            SearchRequest request = null;
            //1.6 定义组合查询对象
            List<Query> queryList = new ArrayList<>();

            //1.6.1 构造分类查询对象
            if (StringUtils.isNotBlank(vo.getCategory())) {
                Query categoryQuery = TermQuery.of(tq -> tq.field("category").value(vo.getCategory()))._toQuery();
                queryList.add(categoryQuery);
            }



            //1.6.3 品牌查询对象
            if (StringUtils.isNotBlank(vo.getBrand())) {
                Query brandQuery = TermQuery.of(bq -> bq.field("brand").value(vo.getBrand()))._toQuery();
                // 添加到查询集合中
                queryList.add(brandQuery);
            }

            if (!CollectionUtils.isEmpty(vo.getSpec())) {
                List<String> spec = vo.getSpec(); // 格式: [规格名:规格选项值]

                for (String s : spec) {
                    //1.8.2 拆分为规格及规格选项
                    String[] split = s.split(":");
                    //1.8.3 构造查询对象
                    Query specQuery = TermQuery.of(sq -> sq.field("specMap." + split[0] + ".keyword").value(split[1]))._toQuery();
                    //1.8.4 添加进queryList
                    queryList.add(specQuery);
                }

            }
            // 1.9 价格区间查询
            if (StringUtils.isNotBlank(vo.getPrice())) {
                //1.9.1 首先得到价格区间  格式: 1000-2000
                String price = vo.getPrice();
                // 1.9.2 对字符串进行拆分
                //  split[0]-->1000 split[1]-->2000
                String[] split = price.split("-");
                //1.9.3 进行区间查询  因为存在 >3000的情况(*),所以分情况进行判断
                Query priceQuery = null;
                if (split[1].equals("*")) {
                    priceQuery = RangeQuery.of(rq -> rq.field("price").gte(JsonData.fromJson(split[0])))._toQuery();
                } else {
                    priceQuery = RangeQuery.of((rq -> rq.field("price").gte(JsonData.fromJson(split[0])).lte(JsonData.fromJson(split[1]))))._toQuery();
                }

                queryList.add(priceQuery);
            }
            // 2.0  定义排序集合
            List<SortOptions> sortOptions = new ArrayList<>();

            // 2.1 价格排序查询
            if (StringUtils.isNotBlank(vo.getSort())) {
                //2.1.1 得到排序字段进行拆分  price:asc
                String sort = vo.getSort();
                String[] split = sort.split(":");
                SortOptions options = null;
                if (split[1].equals("asc")) {
                     options = SortOptions.of(so -> so.field(f -> f.field(split[0]).order(SortOrder.Asc)));
                } else {
                     options = SortOptions.of(so -> so.field(f -> f.field(split[0]).order(SortOrder.Desc)));
                }
                //2.0.2 将options放到排序集合中
                sortOptions.add(options);
            }

            // 取得分页参数
            int page = vo.getPage();
            int pageSize = vo.getPageSize();


            //2.2 开始查询(关键字查询)
            if (StringUtils.isNotBlank(vo.getKeywords())) {
                // 2.2 多字段匹配查询
                Query multiMatchQuery = MultiMatchQuery.of(mmq -> mmq.fields("title", "brand", "category").query(vo.getKeywords()))._toQuery();
                // 添加到查询集合中
                queryList.add(multiMatchQuery);

                request = SearchRequest.of(s -> s.index("item")
                        //① 查询所有
                        .query(q -> q.matchAll(ma -> ma))
                        //② 布尔组合查询(过滤查询)
                        .query(q -> q.bool(bq -> bq.filter(queryList)))
                        //③ 聚合查询(参数key：聚合查询的名字)
                        .aggregations("categoryAgg", agg -> agg.terms(t -> t.field("category")))
                        //④ 高亮查询(参数fields中的key，指的是字段名，preTags：代表前缀字段 postTags: 代表后缀字段)
                        .highlight(h -> h.preTags("<span style='color:red'>").postTags("</span>").fields("title", HighlightField.of(f -> f)))
                        // ⑤ 排序
                        .sort(sortOptions)
                        //⑥ 分页
                        .from((page - 1) * pageSize)
                        .size(pageSize)
                );


            } else {
                request = SearchRequest.of(s -> s.index("item")
                        .query(q -> q.matchAll(ma -> ma))
                );
            }


            //1.4 得到高亮查询结果
            searchResponse = client.search(request, ItemEntity.class);
            //1.5 处理查询结果
            HitsMetadata<ItemEntity> hits = searchResponse.hits();
            //1.6 得到聚合的对象
            Aggregate categoryAgg = searchResponse.aggregations().get("categoryAgg");
            //1.6.1 处理分类数据
            if (categoryAgg != null) {
                List<StringTermsBucket> termsBuckets = categoryAgg.sterms().buckets().array();
//                System.out.println("termsBuckets = " + termsBuckets);
                for (StringTermsBucket termsBucket : termsBuckets) {
                    categoryList.add(termsBucket.key());
                }
            }
            //1.4 得到分类，根据分类名得到模板id，从而得到规格列表、品牌列表
            String category = vo.getCategory();
            if (StringUtils.isBlank(category)) {
                if (categoryList.size() > 0) {
                    category = categoryList.get(0);
                }
            }
            //1.4.1 根据分类名找到品牌及规格列表
            Map<String, List> brandAndSpecList = findBrandAndSpecListByCategory(category);
            //1.4.2 将品牌及规格列表放到map中
            resultMap.putAll(brandAndSpecList);

            //得到分页查询的总页数
            long total = hits.total().value();

            int totalPage = (int) Math.ceil(total / pageSize);

            for (Hit<ItemEntity> hit : hits.hits()) {
                //1.5.0 得到原对象
                ItemEntity entity = hit.source();
                //1.5.1 得到高亮查询数据
                Map<String, List<String>> highlight = hit.highlight();
                //1.5.2 根据字段名获取值
                List<String> heightV = highlight.get("title");
                if (!CollectionUtils.isEmpty(highlight)) {
                    entity.setTitle(heightV.get(0));
                }


                //1.5.3 将高亮对象放到itemList中
                itemList.add(entity);
            }

            // 1.6 将总记录数 和 总页数放入resultMap中
            resultMap.put("total",total);
            resultMap.put("totalPage", totalPage);

        }




        //1.5 将查询结果放到resultMap
        resultMap.put("itemList", itemList);
        resultMap.put("categoryList", categoryList);

        //1.6 返回
        return resultMap;
    }

    //2. 根据分类名得到规格及品牌列表
    private Map<String, List> findBrandAndSpecListByCategory(String category) {
        Map<String, List> map = new HashMap<>();
        if (StringUtils.isNotBlank(category)) {
            //2.1 根据分类名得到模板id
            BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps("itemCatList");
            String typeId = boundHashOperations.get(category);
            //2.2 根据模板id查询出规格列表及品牌列表
            BoundHashOperations<String, String, String> brandOperation = redisTemplate.boundHashOps("brandList");
            String brands = brandOperation.get(typeId);
            //2.2.1 转换为集合
            List<Map> brandList = JSON.parseArray(brands, Map.class);
            //2.2.2 将集合放到map中
            map.put("brandList", brandList);
            //2.3 根据模板id查询出规格列表
            BoundHashOperations<String, String, String> specOperation = redisTemplate.boundHashOps("specList");
            String specs = specOperation.get(typeId);
            //2.3.1 转换为集合
            List<Map> specList = JSON.parseArray(specs, Map.class);
            //2.3.2 将集合放到map中
            map.put("specList", specList);
        } else {
            map.put("specList", new ArrayList());
            map.put("brandList", new ArrayList());
        }
        //2.4 返回
        return map;
    }
}
